#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>

#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " "
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define EXIT_CODE 44

#define NONE -1
#define IN_RDIR     0
#define OUT_RDIR    1
#define APPEND_RDIR 2

int lastcode=0;
int quit=0;
extern char **environ;
char commandline[LINE_SIZE];
char *argv[ARGC_SIZE];
char pwd[LINE_SIZE];
char*rdirfilename=NULL;
int rdir =NONE;

//自定义环境变量表
char myenv[LINE_SIZE];

const char *getusername()
{
    return getenv("USER");
}

const char *gethostname1()
{
    return getenv("HOSTNAME");
}

void getpwd()
{
    getcwd(pwd,sizeof(pwd));
}

void check_redir(char*cmd)
{
    char*pos=cmd;
    while(*pos)
    {
        if(*pos=='>')
        {

        if(*(pos+1)=='>'){
            //将重定向符号置0，分割指令
            *pos++='\0';
            *pos++='\0';
            while(isspace(*pos)) pos++;
            rdirfilename=pos;
            rdir=APPEND_RDIR;
            break;
          }
        else{
            *pos++='\0';
            while(isspace(*pos)) pos++;
            rdirfilename=pos;
            rdir=OUT_RDIR;
            break;
          }
        }
        else if(*pos=='<')
        {
            *pos++='\0';
            while(isspace(*pos)) pos++;
            rdirfilename=pos;
            rdir=IN_RDIR;
            break;
        }
        else{
           // do nothing;
        }
        pos++;
    }
}

void interact(char *cline,int size)
{
    getpwd();
    printf(LEFT"%s@%s %s"RIGHT""LABLE" ",getusername(),gethostname1(),pwd);
    char *s =fgets(cline,size,stdin);
    assert(s);
    (void)s;
    cline[strlen(cline)-1]='\0';

    check_redir(cline);
}

int splitstring(char cline[],char *_argv[])
{
    int i=0;
    argv[i++]=strtok(cline,DELIM);
    while(_argv[i++]=strtok(NULL,DELIM));
    return i-1;
}

void NormalExcute(char* _argv[])
{
    pid_t id=fork();
    if(id<0){
        perror("fork");
        return;
    }
    else if(id==0){
        int fd=0;
        if(rdir==IN_RDIR)
        {
            fd=open(rdirfilename,O_RDONLY);
            dup2(fd,0);
        }
        else if(rdir==OUT_RDIR)
        {
            fd=open(rdirfilename,O_CREAT|O_WRONLY|O_TRUNC,0666);
            dup2(fd,1);
        }
        else if(rdir==APPEND_RDIR)
        {
            fd=open(rdirfilename,O_CREAT|O_WRONLY|O_APPEND,0666);
            dup2(fd,1);
        }
        //子进程执行命令
        execvp(_argv[0],_argv);
        exit(EXIT_CODE);//判断进程替换是否成功
    }
    else{
        int status =0;
        pid_t ret=waitpid(id,&status,0);
        if(ret==id)
        {
            lastcode=WEXITSTATUS(status);
        }
    }
}

int buildCommand(char *_argv[],int _argc)
{
    if(_argc==2&&strcmp(_argv[0],"cd")==0){
        chdir(argv[1]);
        getpwd();
        //更改系统内环境变量内容
        sprintf(getenv("PWD"),"%s",pwd);
        return 1;
    }
    else if(_argc==2&&strcmp(_argv[0],"export")==0)
    {
        strcpy(myenv,_argv[1]);
        putenv(myenv); 
        return 1;
    }
    else if(_argc==2&&strcmp(_argv[0],"echo")==0){
        if(strcmp(_argv[1],"$?")==0)
        {//模拟查看退出码情境
            printf("%d\n",lastcode);
            lastcode=0;
        }
        else if(*_argv[1]=='$')
        {
            char *val=getenv(_argv[1]+1);
            if(val) printf("%s\n",val);
        }
        else{
            printf("%s\n",_argv[1]);
        }
        return 1;
    }

    //特殊处理一下ls指令
    if(strcmp(_argv[0],"ls")==0)
    {
        _argv[_argc++]="--color";
        _argv[_argc]=NULL;
    }
    return 0;
}

int main()
{
    while(!quit)
    {
        //1.交互问题获取命令行
        interact(commandline,sizeof(commandline));
        //2.字串分割问题，解析命令行
        int argc=splitstring(commandline,argv);
        if(argc==0) continue;
        //3.指令的判断，内健命令
        int n=buildCommand(argv,argc);
        //4.普通命令的执行
        if(!n) NormalExcute(argv);
    }
    return 0;
}
